package com.hp.TrimClient;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import com.google.gson.JsonSyntaxException;

public class TrimHttp {

	private org.apache.http.auth.Credentials _credentials;
	private String _baseUri;

	private CloseableHttpClient httpclient;
	private HttpClientContext _clientContext;
	private HttpHost _host;
	private String _path;

	public TrimHttp(String baseUri, org.apache.http.auth.Credentials credentials, Boolean ignoreSSLErrors) throws URISyntaxException, KeyManagementException {
		_credentials = credentials;
		_baseUri = baseUri;

		initHttpClient(ignoreSSLErrors);
	}

	private String handleResponse(HttpResponse response)
			throws ClientProtocolException, IOException {

		int statusCode = response.getStatusLine().getStatusCode();
		
		HttpEntity entity = response.getEntity();
		String responseBody = "";
		
        if (entity != null) {
        	responseBody = EntityUtils.toString(entity);           
            
        }
	
		if (statusCode == 200) {
			return responseBody;
		} else {
			
			TrimJson trimJson = new TrimJson();
			throw trimJson.makeHttpEx(responseBody);
		}
	}
	
	private String getStringResponse(HttpUriRequest httpRequest)
			throws ClientProtocolException, IOException {

		httpRequest.setHeader("Accept", "application/json");
		CloseableHttpResponse responseBody = null;

		try {
			
			responseBody = httpclient.execute(_host, httpRequest,
					_clientContext); // httpRequest, new TrimResponseHandler());

			return handleResponse(responseBody);
		} finally {
			if (responseBody != null) {
				responseBody.close();
			}
		}
	}


	public String doGet(String relativeUri) throws ClientProtocolException,
			IOException {

		HttpGet httpget = new HttpGet(_path + relativeUri);

		return getStringResponse(httpget);

	}


	public String doPost(String relativeUri, Object object, File file)
			throws ClientProtocolException, IOException {

		HttpEntityConverter converter = new HttpEntityConverter(object, file);

		HttpPost httppost = new HttpPost(_path + relativeUri);

		httppost.setEntity(converter.toEntity());

		return getStringResponse(httppost);

	}

	public String doPost(String relativeUri, Object object)
			throws ClientProtocolException, IOException
	{

		return doPost(relativeUri, object, null);
	}


	public String downloadFile(String relativeUri, String filePath)
			throws ClientProtocolException, IOException, URISyntaxException {
		StringBuilder sb = new StringBuilder(_path + relativeUri);

		HttpGet httpget = new HttpGet(sb.toString());
		CloseableHttpResponse response = null;

		try {
			response = httpclient.execute(_host, httpget, _clientContext);
			HttpEntity entity = response.getEntity();

			String downloadPath = null;

			if (response.containsHeader("Content-Disposition")) {
				Header header = response.getFirstHeader("Content-Disposition");

				ContentDisposition contentDisp = new ContentDisposition(header);

				String fileName = contentDisp.getFileName();

				FileOutputStream fos = null;
				try {
					File file = new File(filePath, fileName);
					downloadPath = file.getAbsolutePath();
					fos = new FileOutputStream(file);

					fos.write(EntityUtils.toByteArray(entity));

				} finally {
					if (fos != null) {
						fos.close();
					}
				}

			}

			return downloadPath;
		} finally {
			if (response != null) {
				response.close();
			}
		}
	}

	private void initHttpClient(Boolean ignoreSSLErrors) throws URISyntaxException {

		HttpClientBuilder builder = HttpClientBuilder.create();

		builder.addInterceptorFirst(new HttpRequestInterceptor() {

			public void process(final HttpRequest request,
					final HttpContext context) throws HttpException,
					IOException {
				if (!request.containsHeader("Accept-Encoding")) {
					request.addHeader("Accept-Encoding", "gzip");
				}
			}

		});


		if (ignoreSSLErrors) {		
			SSLContextBuilder sslbuilder = new SSLContextBuilder();
			SSLConnectionSocketFactory sslsf = null;
			try {
				sslbuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());

				sslsf = new SSLConnectionSocketFactory(
						sslbuilder.build());
			} catch (KeyManagementException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			catch (KeyStoreException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			catch (NoSuchAlgorithmException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

			if (sslsf != null) {
				builder.setSSLSocketFactory(sslsf);
			}

		}
		builder.addInterceptorFirst(new HttpResponseInterceptor() {

			public void process(final HttpResponse response,
					final HttpContext context) throws HttpException,
					IOException {
				HttpEntity entity = response.getEntity();
				StatusLine statusLine = response.getStatusLine();

				if (statusLine.getStatusCode() > 401) {
					if (entity != null) {
						String retVal = EntityUtils.toString(entity);
						try {
							TrimJson trimJson = new TrimJson();
							throw trimJson.makeHttpEx(retVal);
						} catch (JsonSyntaxException e) {
							int titleStart = retVal.indexOf("<title>");
							if (titleStart > -1) {
								retVal = retVal.substring(titleStart + 7,
										retVal.indexOf('<', titleStart + 7));
							}
							throw new HttpResponseException(statusLine
									.getStatusCode(), retVal);
						}
					}
				}

				if (entity != null) {
					Header ceheader = entity.getContentEncoding();
					if (ceheader != null) {
						HeaderElement[] codecs = ceheader.getElements();
						for (int i = 0; i < codecs.length; i++) {
							if (codecs[i].getName().equalsIgnoreCase("gzip")) {
								response.setEntity(new GzipDecompressingEntity(
										response.getEntity()));
								return;
							}
						}
					}
				}
			}

		});

		
		httpclient = builder.build();

		if (_clientContext == null) {
			CredentialsProvider credsProvider = new BasicCredentialsProvider();
			credsProvider.setCredentials(AuthScope.ANY, _credentials);

			_clientContext = HttpClientContext.create();
			_clientContext.setCredentialsProvider(credsProvider);

			URI uri = new URI(_baseUri);

			_path = uri.getPath();
			
			_host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
		}


	}

	public void dispose() {
		if (httpclient != null) {
			// When HttpClient instance is no longer needed,
			// shut down the connection manager to ensure
			// immediate deallocation of all system resources

			// httpclient.getConnectionManager().shutdown();

			try {
				((CloseableHttpClient) httpclient).close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				// e.printStackTrace();
			}
		}
	}

}
